/*******************************************************************************

  
  Copyright(c) 2006 - 2007 Semptian Technologies. All rights reserved.
  
  This program is free software; you can redistribute it and/or modify it 
  under the terms of the GNU General Public License as published by the Free 
  Software Foundation; either version 2 of the License, or (at your option) 
  any later version.
  
  This program is distributed in the hope that it will be useful, but WITHOUT 
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
  more details.
  
  You should have received a copy of the GNU General Public License along with
  this program; if not, write to the Free Software Foundation, Inc., 59 
  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  
  The full GNU General Public License is included in this distribution in the
  file called LICENSE.
  
  Contact Information:
  SempGate NSA SDK <nsa@semptian.com>

*******************************************************************************/

#include "nsa.h"
#include "nsa_spi.h"
#include "nsa_npcp.h"

struct nsa_dna _dna;
char nsa_driver_name[] = "nsa";
static char nsa_driver_string[] = "SempGate NSA driver";
char nsa_driver_version[] = NSA_DRV_VERSION;
static char nsa_copyright[] = "Copyright (c) 2006-2007 Semptian Technologies.";


MODULE_AUTHOR("Semptian Technologies, <nsa@semptian.com>");
MODULE_DESCRIPTION("Semptian SempGate Network Security Processor (NSA) controller");
MODULE_LICENSE("GPL");
MODULE_VERSION(NSA_DRV_VERSION);

module_param_named(debug, nsa_debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");

enum {
	IFLISTSZ = 1024,
};
#define WHITESPACE " \t\v\f\n"
#define MAX_SCI_NUMS	4
#define npcp_npdev_probed(npcp)     { npcp->recv_data_ready = 1;}
#define npcp_npdev_unprobed(npcp)    { npcp->recv_data_ready = 0;}
#define kmalloc(s,...) malloc(s)
#define	BUG_ON(x)
#define vmalloc(x,...) malloc(x)
#define	KERN_ERR
#define	NPRINTK(s,...)
#define netdev_priv(dev) dev->priv

static char sci_iflist[IFLISTSZ]=NSA_SCI_NAME;
module_param_string(sci_iflist, sci_iflist, IFLISTSZ, 0600);
MODULE_PARM_DESC(sci_iflist, "sci_iflist=\"em0 [em1 ...]\"\n");


/* enabler user control init and deinit */
static int nsa_ctrl_init(void);
static void nsa_ctrl_deinit(void);

static void nsa_aging_task(struct work_struct *work);
static int nsa_counter;
struct nsa_enabler *nsa_scis[MAX_SCI_NUMS]; 

#if 0
static struct packet_type npcp_packet_type = {
	.type		= __constant_htons(ETH_P_NPCP),
	.func		= npcp_proto_recv,
};
#endif

static void npcp_recv_flow(struct npcp_controller *npcp, struct sk_buff *skb)
{
    return;
}


static void npcp_send_flow(struct npcp_controller *npcp, int type)
{
    return;
}


/** 
 * npcp_recv - npcp rx packet process, called by devX
 * 
 * @npcp: npcp protocol control block, may derived from netdev
 * @skb:  packet buffer from device, ether header processed 
 * 
 * @return 0 - success 
 *  
 * Rcv handler will verify sequence number for all packets, 
 * process flow control immediately, and put other packets into 
 * a queue, then trigger soft irq to enable up layer to process 
 * further 
 */
void  npcp_recv(struct npcp_controller *npcp, struct sk_buff *skb)
{

	u_int8_t type = npcp_type(skb);
	u_int16_t host_tx = npcp_host_tx(skb);
	u_int16_t np_tx = npcp_np_tx(skb);

	int flags;
    int len;
	NPRINTK(npcp, NPCP, INFO, 
               "packet type 0x%x, host_tx=0x%x np_tx=0x%x\n", 
			   type, host_tx, np_tx);

    len = npcp_len(skb);
    if(skb->len != len+NPCP_HEADER_SIZE) {
        printk(KERN_ERR "npcp recved packet w/t wrong length: (%d)!=(%d)\n",
               skb->len, len);
		dump_skb(__FUNCTION__,skb,skb->len);
		dev_kfree_skb(skb);
		return;
    }

	switch(npcp_type_mask(type)){
	case NPCP_FLOW:
		npcp_recv_flow(npcp, skb);
		dev_kfree_skb(skb);
        break;
	case NPCP_DATA:
		if(!npcp->recv_data_ready) {
			dev_kfree_skb(skb);
			return;
		}
		//dump_skb(__FUNCTION__,skb,skb->len);
        spin_lock_irqsave(&npcp->seq_lock, flags);
        if(npcp->rx_seq == np_tx) {
            /* recved a good nsa packet seq. */
            incr_modulo(npcp->rx_seq, NPCP_RX_RING_SIZE);;
        }else{
            /* previous packet from nsa missed, the only things we can do  
             * are couting and complaining but should not drop.
             */
            npcp->np_tx_loss++;
            npcp_send_flow(npcp, NPCP_FLOW_NACK);
        }
        spin_unlock_irqrestore(&npcp->seq_lock, flags);
		
        npcp->rx_data++;
		npcp->tx_ed = host_tx;
        //schedule_work(&npcp->confirm_task);
        (*npcp->np_dev_recv_data)(npcp->np_dev, skb);      /* process packet */
        
		break; 
    case NPCP_CTL:
        npcp->rx_ctrl++;
		//dump_skb(__FUNCTION__,skb,200);
		if(npcp->expected_seq == host_tx) {
	        npcp->buff = skb;
		}else{
            /* There is a packet missing, set indicator for sender, release
             * the wrong buffer.
			 */
            printk(KERN_ERR "npcp control packet out of order: host_tx=0x%x\n", host_tx);
			dump_skb(__FUNCTION__,skb,skb->len);
            dev_kfree_skb(skb);
            set_bit(NPCP_SENDING_ACKED_ERR, &npcp->sending_state);
		}
        npcp->buff = skb;
		/* anyway let blocking sender go! */
		set_bit(NPCP_SENDING_ACKED, &npcp->sending_state);
//        wake_up(&npcp_control_queue);
		break;
	default:
        printk(KERN_ERR "unknown npcp type 0x%x\n", type);
		dev_kfree_skb(skb);
    }
}


static int npcp_proto_recv(struct sk_buff *skb, struct net_device *dev, 
                           struct packet_type *pt, struct net_device *orig_dev)
{
    /* it should use devX's name to index a proper npcp */
    struct npcp_controller *npcp = npcp_by_index(0);

    skb_push(skb, sizeof(struct ethhdr));
    if(npcp){
        npcp_recv(npcp, skb);
    }else{
        //printk(KERN_ERR "NPCP packet from unknow interface %s\n", dev->name);
        kfree_skb(skb);
    }
    return 0;
}

/**
 * nsa_npcp_init - init nsa controller factory
 * 
 * @return 0 - Succuess
 */
int nsa_init()
{
    int i;
    
    for(i=0; i<MAX_SCI_NUMS; i++)
    {
        nsa_scis[i] = NULL;
		nsa_counter++;
    }
    return 0;
}
#if 0
void npcp_setup_protocol()
{
    dev_add_pack(&npcp_packet_type);
}
#endif

#if 0
static void npcp_setup_sci_ring( struct net_device *netdev)
{
	int ret;
	struct ethtool_ringparam ringparam;

	netdev->ethtool_ops->get_ringparam(netdev,&ringparam);
	ringparam.tx_pending = ringparam.tx_max_pending;
	ringparam.rx_pending = ringparam.rx_max_pending;
	ret = netdev->ethtool_ops->set_ringparam(netdev,&ringparam);
	if( ret ) {
		printk(KERN_ERR "npcp set sci tx/rx ring ERR\n");
	}
	return;
}
#endif

struct net_device *npcp_setup_sci(char *name)
{
    struct net_device *netdev;

#ifdef LINUX_NEW_GET_DEV
    netdev = dev_get_by_name(&init_net,name);
#else
    netdev = dev_get_by_name(name);   
#endif
    if(!netdev) {
        printk(KERN_ERR "error to find sci interface %s\n", name);
        return NULL;
    }
    
//	dev_set_mtu(netdev, 2000);
//    dev_change_flags(netdev, IFF_PROMISC|IFF_UP);
//	npcp_setup_sci_ring(netdev);
    while(!netif_carrier_ok(netdev)) {
        msleep(10);
    }
    return netdev;
}


/**
 * 
 * npcp_npcp_register - register nsa controller, i.e. npcp cb.
 * 
 * @devX: ethX's netdev
 * 
 * @return nsa controller's pointer
 * 
 * find a free nsa controller slot, register ethernet to
 * controller, allocate npcp_controller for the ethernet, return
 * the nsa control block to the caller, who should have record
 * in its private control block.
 * 
 */
struct npcp_controller *npcp_allocate(int *debug, struct net_device *devX)
{
    int i;
    struct npcp_controller *npcp;
    /* npcp should allocate a free slot, 
     * though slots defined in upper layer */
    for(i=0; i<MAX_SCI_NUMS; i++){
        if(npcp_controllers[i] == NULL )
            break;
    }
    if(i==MAX_SCI_NUMS){
        printk(KERN_ERR "too many NSA cards to register...\n");
        return NULL;
    }

    npcp = kmalloc(sizeof(struct npcp_controller), GFP_ATOMIC);
    if(!npcp){
        printk(KERN_ERR "error to allocate nsa controller interface...\n");
        return NULL;
    }

    memset(npcp, 0, sizeof(*npcp));
	npcp->devX = devX;
    npcp->id = i;
    npcp->msg_enable = debug;

    npcp_controllers[i] = npcp;
 	return npcp;
}

#if 0
static void _init_nsa_q(struct nsa_dev *nsa)
{
	int i;
    struct nsa_q *q;

    for(i=0; i<QUEUE_MAX; i++) {
        q = &nsa->queue[i];
		q->queue->m_next = NULL;
//        skb_queue_head_init(&(q->queue));
        q->maxlen=0;								
        q->type = i;					
        q->limit = _queue_limit[i];		
    }
}
#endif
struct nsa_dev *nsa_dev_allocate(struct npcp_controller *npcp)
{
	struct nsa_dev *nsa;
    
    nsa = kmalloc(sizeof(struct nsa_dev), GFP_ATOMIC);
	if(!nsa){
		printk(KERN_ERR "error to allocate nsa device...\n");
		return NULL;
	}
    memset(nsa, 0, sizeof(*nsa));
//    _init_nsa_q(nsa);

    nsa->dev_id = npcp->id;
    nsa->sci = npcp;
    nsa->msg_enable = npcp->msg_enable;

    return nsa;
}


void npcp_setup_npdev(struct npcp_controller *npcp, 
     caddr_t np_dev, void (*np_dev_recv_data)(caddr_t, struct sk_buff *))
{ 
    npcp->np_dev = np_dev;
    npcp->np_dev_recv_data = np_dev_recv_data;
/*    if(np_dev_recv_data) 
        npcp->np_dev_recv_data = np_dev_recv_data;
    else
        npcp->np_dev_recv_data = npcp_default_recv_data; 
*/
}

/**
 * nsa_init_module - Driver Registration Routine
 *
 * nsa_init_module is the first routine called when the driver
 * is loaded. All it does is init a global data structure and
 * waiting for devX to register.
 * 
 **/


static void nsa_sci_init_dna(struct nsa_dev *nsa, struct net_device *netdev)
{
	struct ethtool_eeprom eeprom;
	struct nsa_dna *pdna = &nsa->dna;

	eeprom.len = sizeof(struct nsa_dna);
	eeprom.offset = NSA_EEPROM_INFO_ADDR;
	e1000_get_eeprom(netdev, &eeprom, (u_int8_t*)pdna);
	return ;
}

struct net_device *get_net_device(char *name)
{
	struct device *if;

	TAILQ_FOREACH(if, &alldevs, dv_list)
	{
		if(strcmp(if->dv_xname, name) == 0)
			return (struct net_device *)if;
	}
	return NULL;			
}

static int __init
nsa_init_module(void)
{
    struct net_device *netdev;
    struct nsa_dev *nsa;
    int  ret=0;
    struct npcp_controller *npcp;
    char *p, *q;

	printk(KERN_INFO "%s - version %s\n",
	       nsa_driver_string, nsa_driver_version);

	printk(KERN_INFO "%s\n", nsa_copyright);

    nsa_init();

#ifdef NSA
    if(nsa_ctrl_init()<0)
        printk(KERN_ERR "error to initialize control interface\n");
#endif
	/* npcp routines, initial npcp protocol handler */
//    npcp_setup_protocol();

    /* setup sci interface(s) as per module parameters */
    p = sci_iflist + strspn(sci_iflist, WHITESPACE);
	for (; *p; p = q + strspn(q, WHITESPACE)) {
		q = p + strcspn(p, WHITESPACE);
        if (q == p)
            break;

        /* first find and setup sci parameters */
//      if(!(netdev = npcp_setup_sci(p))){
        if(!(netdev = get_net_device(p))){
            printk(KERN_ERR "error to setup sci %s\n", p);
            continue;
        }
		/* allocate npcp controller for each sci netdevice */
        if(!(npcp = npcp_allocate(&nsa_debug, netdev)))
        {
            printk(KERN_ERR "error to initialize npcp controller for sci %s\n", p);
            return -EINVAL;
        }

        /* allocate upper layer control block, in which npcp controll block 
         *  should be accessable.
         */
        if(!(nsa = nsa_dev_allocate(npcp))) {
            printk(KERN_ERR "error to initialize nsa device block on sci %s\n", p);
            return -EINVAL;
        }

        /* 
         * setup a link to upper layer block in npcp controller, 
         * register upper layer receiver callback.
         */
        npcp_setup_npdev(npcp, (caddr_t)nsa, nsa_dev_recv_data);

        /* read dna data from NSA card */
        nsa_sci_init_dna(nsa, netdev);				//need to change, get eeprom!

        /* probe nsa device */
        nsa_probe(npcp);

        /* notify npcp controller nsa probed */
        npcp_npdev_probed(npcp);
	}
    return ret;
}

static inline unsigned char *skb_pull(struct sk_buff *skb, unsigned int len)
{
	skb->len -= len;
	skb->data += len;
	return skb->data;
}
static inline unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
{
	skb->len += len;
	skb->data -= len;
	return skb->data;
}

static int nsa_process_pkt(struct nsa_dev *nsa, struct sk_buff * skb)
{
	struct net_device* netdev;
	struct nsa_spi *spi;
    struct nsa_header *head=nsa_dev_header(skb);
    int ifn;

	ifn = nsa_dev_i_pif(head);
	if(ifn<0 || ifn>7){
		nsa->error.unknown_dev++;
		NPRINTK(nsa, DRV, INFO, "unknown interface %d\n", ifn);
        dev_kfree_skb(skb);
		return 1;
	}
    netdev = nsa->spis[ifn];
    if(!netif_running(netdev)) {
        nsa->error.wrong_dev++;
        NPRINTK(nsa, DRV, INFO, "wrong dev, ifn=%d %d\n", ifn, nsa->error.wrong_dev);
        dev_kfree_skb(skb);
        return 1;
    }

    skb->dev= netdev;
   /*	Eleminate the packet head*/
	skb_pull(skb, NSA_HEADER_LEN);
	//skb->next = NULL;
    skb->pkt_type = PACKET_HOST;    /* sci had called eth_type_trans, we need reset it */
    
	//NDUMP(nsa, DRV, _dump_skb, skb);

    spi = netdev_priv(netdev);
	/* stat */
	spi->net_stats.rx_packets++;
	spi->net_stats.rx_bytes += skb->len;

	//need to change! =========zhly
//	skb->protocol = eth_type_trans(skb, netdev);

//    nsa_enabler_ingress(nsa->nsa_handler, skb, head);
    //ndebug("recieve packet type=%d\n", skb->pkt_type);
	/*
	ip=(struct iphdr *)((caddr_t)eth_hdr(skb)+sizeof(struct ethhdr));
	if(ip->protocol == IPPROTO_UDP){
		if(ifn == 0)
			netdev = nsa->spis[1];
		else
			netdev = nsa->spis[0];
		skb->dev = netdev;
		nsa->nsa_handler->nsa_enabler_counter[26]++;
		nsa_spi_xmit_frame(skb,netdev);
	} else {
	*/
	//need change!	============zhly
  //  netif_receive_skb(skb);
    netdev->last_rx = jiffies;
	dev_kfree_skb(skb);
	skb = NULL;
	//}


	return 0;
}


void nsa_dev_recv_data(caddr_t npdev, struct sk_buff *skb)
{
    struct nsa_dev *nsa = (struct nsa_dev*)npdev;
    struct nsa_header *head = nsa_dev_header(skb);
    int type = nsa_dev_pkttype(head);
    switch (type) {
    case NSA_PACKET_T:
    {
        int cause=nsa_dev_cause_id(head);
        switch (cause) {
        case CAUSE_FIRST_PKT:
#if 0
            if(_nsa_tail_queue(nsa, skb, QUEUE_FIRST)==0){
            nsa->drv_stats.rx_drop_first++;
            }
            else
#endif
            {
                nsa->drv_stats.rx_pkts_first++;
            }
            break;
        case CAUSE_MIRROR:
#if 0
            if(_nsa_tail_queue(nsa, skb, QUEUE_MIRROR)==0){
                nsa->drv_stats.rx_drop_mirror++;
            }else
#endif
            {
                nsa->drv_stats.rx_pkts_mirror++;
            }
            break;
        case CAUSE_HOST:
#if 0
            if(_nsa_tail_queue(nsa, skb, QUEUE_HOST)==0){
                nsa->drv_stats.rx_drop_host++;
            }else
#endif
            {
                nsa->drv_stats.rx_pkts_host++;
            }
            break;
        default:
#if 0
            if(_nsa_tail_queue(nsa, skb, QUEUE_GENERIC)==0){
                nsa->drv_stats.rx_drop_general++;
            }else
#endif
            {
                nsa->drv_stats.rx_pkts_general++;
            }
        }
        nsa->drv_stats.rx_pkts++;
        nsa_process_pkt(nsa, skb);
        break;
    }
	case NSA_AGING_T:
		//if(nsa->nsa_handler) 
		//	nsa->nsa_handler->nsa_enabler_counter[5]++;
		skb_pull(skb,NPCP_HEADER_SIZE);
		//nsa_dump_skb_age(skb);
//        if(_nsa_tail_queue(nsa, skb, QUEUE_AGE_T)==0){
        if( 1 ){
            nsa->drv_stats.age_pkts_drop++;
        }else{
            nsa->drv_stats.age_pkts++;
//            schedule_work(&nsa->aging_task);
        }
		
        break;
	default:
		ndebug("type:%d\n",type);
		dev_kfree_skb(skb);	
	/*
	case NSA_LOG_T:
		
        break;
    case NSA_COUNTER_T:
        break;
		*/
    }
	return ;
}

#if 0
static void nsa_aging_task(struct work_struct *work)
{

    struct nsa_dev *nsa = container_of(work, struct nsa_dev, aging_task);
    struct sk_buff_head *queue = &nsa->queue[QUEUE_AGE_T].queue;
    struct sk_buff *skb;

    while((skb=skb_dequeue(queue))) {
        nsa_process_aging(nsa->nsa_handler, skb);
		dev_kfree_skb(skb);
		skb = NULL;
		if(nsa->nsa_handler)
			nsa->nsa_handler->nsa_enabler_counter[7]++;
    }

}

static int nsa_xmit_task(void *data)
{
    struct nsa_dev *nsa = (struct nsa_dev*)data;
    struct sk_buff_head *queue = &nsa->queue[QUEUE_TX_T].queue;
    struct sk_buff *skb;
    struct nsa_header *head;
    int flags;
    int ret;
    int i;
    //static struct sk_buff *hold=NULL;
    struct nsa_enabler *enabler=nsa->nsa_handler;
    DEFINE_WAIT(wait);

    init_waitqueue_head(&nsa->xmit_wait_queue);

    set_current_state(TASK_INTERRUPTIBLE);
    
	while (!kthread_should_stop()) {
        if(nsa->hold) {
            skb = nsa->hold;
            nsa->hold = NULL;
            goto re_send;
        }
		//preempt_disable();
        spin_lock_irqsave(&queue->lock, flags);
		skb=__skb_dequeue(queue);
        enabler->nsa_enabler_counter[26]++;
        if(!skb) { 
            enabler->nsa_enabler_counter[27]++;
            spin_unlock_irqrestore(&queue->lock, flags);
			//preempt_enable_no_resched();
            goto relax;
        }
        head = (struct nsa_header*)skb_push(skb, NSA_HEADER_LEN);
        if(nsa_dev_pkttype(head) == NSA_SESSION_T) {
            i = nsa_dev_cache(head);
            if(i!=0xf) {
                nsa->cache_pos[i] = NULL;
            }	
        }
		nsa_dev_len(head) = skb->len - NSA_HEADER_LEN;
        nsa_dev_cache(head)=0;
		//dump_skb(__FUNCTION__,skb,skb->len);
		spin_unlock_irqrestore(&queue->lock, flags);
		//preempt_enable_no_resched();
    re_send:
        ret = npcp_send_data(nsa->sci, skb, NPCP_RC_SUCCESS_DO, 0, NULL);
        if(ret==NPCP_SEND_BUSY) {
            nsa->hold = skb;
            nsa->error.tx_ssn_busy++;
            goto relax;
        }else{
            enabler->nsa_enabler_counter[23]++;
            continue;
        }
    relax:
        prepare_to_wait(&nsa->xmit_wait_queue, &wait, TASK_INTERRUPTIBLE);
        schedule_timeout(HZ/10);
        finish_wait(&nsa->xmit_wait_queue, &wait);
	}
	//local_bh_enable();

    spin_lock_irqsave(&queue->lock, flags);
    while((skb=__skb_dequeue(queue))) {
        for(i=0; i<MAX_NSA_SSN_CACHE_ENTRIES; i++) {
            if(nsa->cache_pos[i] == skb) {
                nsa->cache_pos[i] = NULL;
            }
        }
        dev_kfree_skb(skb);
    }
    spin_unlock_irqrestore(&queue->lock, flags);
	
    return 0;

}
#endif

/**
 * 
 * nsa_dev_advertise - broadcast packet to find alive nsa
 * 
 * @sci: the interface through which
 * 
 * @return
 * 
 * Through nsa_npcp_register, host knows there is a new nsa sci
 * becoming alive, but host still needs to confirm and setup
 * communication relationship. nsa_host_advertise broadcasts
 * npcp adv. packets until a reply recievs.
 * nsa adv_ack format:
 * |  31   -   16  |  15   -   00    | 
 * |       NSA's DNA string          | 
 * |     ports     | ports map       |
 * in ports map: 1-copper, 0-fiber
 * 
 */
int nsa_dev_probe(struct nsa_dev *nsa)
{
    struct npcp_controller *sci = nsa_sci(nsa);
    u_int8_t tmpa[4];
    DECLARE_SEND(ret, skb, idx);
    START_SEND(skb);
    
        
    npcp_reg_read(skb, idx, SSR_VERSION);
    npcp_reg_read(skb, idx, SSR_DATE);
    npcp_reg_read(skb, idx, SSR_BUILD);
    npcp_reg_read(skb, idx, SSR_MODEL);

    npcp_reg_read(skb, idx, SSR_EPLD_BUILD_LOW);
    npcp_reg_read(skb, idx, SSR_EPLD_BUILD_HIGH);
    npcp_reg_read(skb, idx, SSR_EPLD_DNA0);
    npcp_reg_read(skb, idx, SSR_EPLD_DNA1);
    npcp_reg_read(skb, idx, SSR_EPLD_DNA2);
    npcp_reg_read(skb, idx, SSR_EPLD_DNA3);
    //dump_skb(__FUNCTION__, skb, skb->len);
    DO_SEND(sci, skb, NSA_WAIT_TIMES*4);
    //dump_skb(__FUNCTION__, skb, skb->len);

    printk(KERN_INFO "OK to find NSA: version %08x build %x on %x model %x\n",
           npcp_reg_data(skb,0), npcp_reg_data(skb,2),
           npcp_reg_data(skb,1), npcp_reg_data(skb,3));

    printk(KERN_INFO "\tEPLD version %x %x\n", 
           npcp_reg_data(skb,4),npcp_reg_data(skb,5));

    tmpa[0] = npcp_reg_data(skb,6);
    tmpa[1] = npcp_reg_data(skb,7);
    tmpa[2] = npcp_reg_data(skb,8);
    tmpa[3] = npcp_reg_data(skb,9);

    memcpy(&nsa->hw_dna, tmpa, 4);
    END_SEND(skb);
}


/**
 * 
 * nsa_dev_advertise - broadcast packet to find alive nsa
 * 
 * @sci: the interface through which
 * 
 * @return
 * 
 * Through nsa_npcp_register, host knows there is a new nsa sci
 * becoming alive, but host still needs to confirm and setup
 * communication relationship. nsa_host_advertise broadcasts
 * npcp adv. packets until a reply recievs.
 * nsa adv_ack format:
 * |  31   -   16  |  15   -   00    | 
 * |       NSA's DNA string          | 
 * |     ports     | ports map       |
 * in ports map: 1-copper, 0-fiber
 * 
 */
int nsa_dev_probe(struct nsa_dev *nsa)
{
    struct npcp_controller *sci = nsa_sci(nsa);
    u_int8_t tmpa[4];
    DECLARE_SEND(ret, skb, idx);
    START_SEND(skb);
    
        
    npcp_reg_read(skb, idx, SSR_VERSION);
    npcp_reg_read(skb, idx, SSR_DATE);
    npcp_reg_read(skb, idx, SSR_BUILD);
    npcp_reg_read(skb, idx, SSR_MODEL);

    npcp_reg_read(skb, idx, SSR_EPLD_BUILD_LOW);
    npcp_reg_read(skb, idx, SSR_EPLD_BUILD_HIGH);
    npcp_reg_read(skb, idx, SSR_EPLD_DNA0);
    npcp_reg_read(skb, idx, SSR_EPLD_DNA1);
    npcp_reg_read(skb, idx, SSR_EPLD_DNA2);
    npcp_reg_read(skb, idx, SSR_EPLD_DNA3);
    //dump_skb(__FUNCTION__, skb, skb->len);
    DO_SEND(sci, skb, NSA_WAIT_TIMES*4);
    //dump_skb(__FUNCTION__, skb, skb->len);

    printk(KERN_INFO "OK to find NSA: version %08x build %x on %x model %x\n",
           npcp_reg_data(skb,0), npcp_reg_data(skb,2),
           npcp_reg_data(skb,1), npcp_reg_data(skb,3));

    printk(KERN_INFO "\tEPLD version %x %x\n", 
           npcp_reg_data(skb,4),npcp_reg_data(skb,5));

    tmpa[0] = npcp_reg_data(skb,6);
    tmpa[1] = npcp_reg_data(skb,7);
    tmpa[2] = npcp_reg_data(skb,8);
    tmpa[3] = npcp_reg_data(skb,9);

    memcpy(&nsa->hw_dna, tmpa, 4);
    END_SEND(skb);
}

static int nsa_dev_reset_base(struct nsa_dev *nsa)
{
    struct npcp_controller *sci = nsa_sci(nsa);
    DECLARE_SEND(ret, skb, idx);
    START_SEND(skb);/*Reset SempSec*/


    //Disable all if of SempSec and set the initial IFG config
    npcp_reg_write(skb, idx, SSR_PHY_IF0, 0x20);
    npcp_reg_write(skb, idx, SSR_PHY_IF1, 0x20);
    npcp_reg_write(skb, idx, SSR_PHY_IF2, 0x20);
    npcp_reg_write(skb, idx, SSR_PHY_IF3, 0x20);
    npcp_reg_write(skb, idx, SSR_PHY_IF4, 0x20);
    npcp_reg_write(skb, idx, SSR_PHY_IF5, 0x20);
    npcp_reg_write(skb, idx, SSR_PHY_IF6, 0x20);
    npcp_reg_write(skb, idx, SSR_PHY_IF7, 0x20);

	/*Reset and Init PHY0*/
	npcp_reg_write_wait(skb, idx, SSR_RESET_PHY0, NSA_RESET_CMD|REG_WAIT_REQ);
	/*Reset PHY1*/
    npcp_reg_write_wait(skb, idx, SSR_RESET_PHY1, NSA_RESET_CMD|REG_WAIT_REQ);
	//npcp_reg_write_wait(skb, idx, SSR_RESET_SEMPSEC, NSA_RESET_CMD|REG_WAIT_REQ);
    //dump_skb(__FUNCTION__, skb, skb->len);
    DO_SEND(sci, skb, NSA_WAIT_TIMES*10);

    if(!npcp_reg_result_ok(skb,0)){
            ERROR_SEND(skb);
    }
    END_SEND(skb);
	
}

static int nsa_reset_sempsec(struct nsa_dev *nsa)
{
	struct npcp_controller *sci = nsa_sci(nsa);
	DECLARE_SEND(ret, skb, idx);
    START_SEND(skb);/*Reset SempSec*/

	npcp_reg_write_wait(skb, idx, SSR_RESET_SEMPSEC, NSA_RESET_CMD|REG_WAIT_REQ);
	//dump_skb(__FUNCTION__, skb, skb->len);
	DO_SEND(sci, skb, NSA_WAIT_TIMES*10);
	//dump_skb(__FUNCTION__, skb, skb->len);
	if(!npcp_reg_result_ok(skb,0)){
            ERROR_SEND(skb);
    }
    END_SEND(skb);
}

static int nsa_dev_reset_tcam(struct nsa_dev *nsa)
{
    struct npcp_controller *sci = nsa_sci(nsa);
    DECLARE_SEND(ret, skb, idx);
    START_SEND(skb);
    npcp_reg_write_wait(skb,idx, SSR_RESET_TCAM, NSA_RESET_CMD|REG_WAIT_REQ);
    DO_SEND(sci, skb, NSA_WAIT_TIMES);
    if(!npcp_reg_result_ok(skb,0)){
        ERROR_SEND(skb);
    }
    END_SEND(skb);
}


/* waiting  for chip be initailed  otherwise nsa insmod will be stop*/
static int nsa_init_check(struct nsa_dev *nsa)
{
	u_int32_t data;
	int i=0, ret;

	while(i < 100){
		if((ret = nsa_hw_read_reg(nsa,SSR_INIT,&data))) 
			return ret;
		if(data == 0x7ff)
			return 0;
		msleep(10);
		i++;
	};
	return -2;
}

int nsa_hw_write_mdio(struct nsa_dev *nsa, u_int16_t ifn,
                   u_int16_t reg,
                   uint16_t phy_data)
{
	
	u_int32_t tmpchannel;
	u_int32_t tmpaddr;
	u_int32_t tmpreg;

    DECLARE_SEND(ret, skb, idx);

	if(nsa->status & NSA_STATUS_FLASH_UPGRADING)
		return -1;

    START_SEND(skb);

    tmpchannel = (ifn & ((1<<5) - 1));

	if (tmpchannel < 4)
	    tmpreg = SSR_MDIO_PHY0_CTRL;
	else
	    tmpreg = SSR_MDIO_PHY1_CTRL;

    tmpaddr = (tmpchannel<<5) | (reg & ((1<<5) - 1));
    npcp_reg_write(skb, idx, (tmpreg+0x4), phy_data&0xffff);
	npcp_reg_write_wait(skb,idx, tmpreg, (REG_WRITE_REQ|tmpaddr));
	//dump_skb(__FUNCTION__, skb, skb->len);
    
    DO_SEND(nsa->sci, skb, NSA_WAIT_TIMES*4);

	//dump_skb(__FUNCTION__, skb, skb->len);
    if(!npcp_reg_result_ok(skb,1))
    {
        ERROR_SEND(skb);
    }
    END_SEND(skb);
}

static int nsa_dev_reset_mdio(struct nsa_dev *nsa)
{
    int phy;
    for(phy=0; phy<8; phy++) {
        if(nsa_hw_write_mdio(nsa, phy, PHY_REG_XSPECIFIC_CTRL, PHY_INIT_DATA0)<0)
            return -1;
        if(nsa_hw_write_mdio(nsa, phy, PHY_REG_CTRL, PHY_SRESET_CMD)<0)
            return -1;
        if(phy>=4) {
            /* manual set phy's led for sfp*/
            if(nsa_hw_write_mdio(nsa, phy, 24, 0x1)<0)
                return -1;
        }
	}
    return 0;
}

/*
 * This routine resets the hardware. It cleans all registers, internal memory and external memory.
 * It also resets phy controllers and other chips. When it completes, NSA is clean, empty and ready
 * to receive initializing. 
 */
int nsa_dev_reset(struct nsa_dev *nsa)
{
    int ret=0;
    ret = nsa_dev_reset_base(nsa);
    if(ret<0) {
        return ret;
    }
	msleep(1000);
	ret = nsa_reset_sempsec(nsa);
	if(ret < 0) {
		return ret;
	}
    if(nsa->dna.tcam == TCAM_VALID){
        nsa_dev_reset_tcam(nsa);
        msleep(1000);
    }
    ret = nsa_init_check(nsa);
	if(ret <0) 
		return ret;
    ret = nsa_dev_reset_mdio(nsa);
    if(ret<0) {
        return ret;
    }
    return ret;
}

int nsa_dev_init(struct nsa_dev *nsa)
{
	nsa_hw_write_reg(nsa, SSR_AGING_CTRL, 0x24);
    nsa_hw_write_reg(nsa, SSR_RXTX_BEGIN, 0x6);
	nsa_hw_write_reg(nsa, SSR_PKTUP_CTRL, 0x2);
	/*bypass qos*/
	//nsa_hw_write_reg(nsa, 0x130000, 0x1);
#if 0
	if(nsa->dna.tcam == TCAM_VALID) {
        nsa_hw_tcam_init(nsa);
    }
#endif
	return 0;
}

static void nsa_lif_init(struct nsa_enabler *enabler)
{
	int i;
    struct net_device **lif_pool = enabler->nsa_lif_pool;
	for(i=0; i<NSA_MAX_IF_NUM; i++)
		lif_pool[i] = NULL;
}

struct nsa_enabler *nsa_enabler_allocate(int dev_id)
{
    struct nsa_enabler *enabler;
    BUG_ON(nsa_scis[dev_id]);
    enabler = vmalloc(sizeof(*enabler));
    if(!enabler) {
        return NULL;
	}
    memset(enabler, 0, sizeof(*enabler));
//    spin_lock_init(&enabler->nsa_ssn_lock);
#if 0
    if(nsa_init_cache_ssn(enabler)<0){
        printk(KERN_ERR "error to init nsa cache...\n");
        vfree(enabler);
        return NULL;
    }
#endif
    enabler->nsa_id = dev_id;
    enabler->lif_resource = NSA_MAX_PIF_NUM+NSA_MAX_LA_NUM+1; /* 1 - 8 reserved for LA */
    enabler->msg_enable = &nsa_debug;
    nsa_lif_init(enabler);
//    nsa_enabler_proc_init(enabler);
    nsa_scis[dev_id]=enabler;
	return enabler;
}


int nsa_phy_reset(struct nsa_spi *spi)
{
    struct nsa_dev *nsa = spi->nsa;
    u_int8_t phy = spi->ifn;

	if(spi->hw.media_type == nsa_media_type_fiber) {
		if(nsa_hw_write_mdio(spi->nsa, spi->ifn, 0x1b, 0xb003) < 0)
			return -1;
	}
    if(nsa_hw_write_mdio(nsa, phy, PHY_REG_XSPECIFIC_CTRL, PHY_INIT_DATA0)<0)
        return -1;
    if(nsa_hw_write_mdio(nsa, phy, PHY_REG_CTRL, PHY_SRESET_CMD)<0)
        return -1;

    return 0;
}
void nsa_spi_reset(struct net_device *netdev)
{
    struct nsa_spi *spi = netdev_priv(netdev);
    nsa_phy_reset(spi);
}


int nsa_hw_spm(struct nsa_dev *nsa, int action, u_int8_t table_id,
                   u_int32_t addr, u_int32_t *data)
{
    struct _spm_req tmp;
    int spm_reg_delta=0;

    DECLARE_SEND(ret, skb, idx);
    START_SEND(skb);

    if(table_id > NSA_SPM_ID0) 
       spm_reg_delta = SSR_SPM1_DELTA;

    if((table_id > NSA_SPM_ID_MAX)||(table_id<NSA_SPM_ID_MIN)){
        printk(KERN_ERR "invalid spm table id\n");
        return -EINVAL;
    }
    tmp.data = 0;
    tmp.table_id = (table_id & 0xf);
    tmp.addr = (addr & 0xfffff);

    switch (action) {
    case NSA_HW_ACTION_READ:
        npcp_reg_write(skb, idx,(SSR_SPM_ADDR+spm_reg_delta), tmp.data);
        npcp_reg_write_wait(skb, idx, (SSR_SPM_CTRL+spm_reg_delta), REG_READ_REQ);
        npcp_reg_read(skb, idx, (SSR_SPM_DATA+spm_reg_delta));

		//dump_skb(__FUNCTION__, skb, 64);
        DO_SEND(nsa->sci,skb, NSA_WAIT_TIMES*10);
		//dump_skb(__FUNCTION__, skb, 64);

        if(!npcp_reg_result_ok(skb,1)){
            ERROR_SEND(skb);
        }
        *data = npcp_reg_data(skb, 2);
        break;
    case NSA_HW_ACTION_WRITE:
        npcp_reg_write(skb, idx, (SSR_SPM_ADDR+spm_reg_delta), tmp.data);
        npcp_reg_write(skb, idx, (SSR_SPM_DATA+spm_reg_delta), *data);
        npcp_reg_write_wait(skb, idx, (SSR_SPM_CTRL+spm_reg_delta), REG_WRITE_REQ);
		//dump_skb(__FUNCTION__, skb, 64);
        DO_SEND(nsa->sci,skb, NSA_WAIT_TIMES*10);
		//dump_skb(__FUNCTION__, skb, 64);
        if(!npcp_reg_result_ok(skb,1) ){
            ERROR_SEND(skb);
        }
        break;
    default:
        printk(KERN_ERR "bad action on spm\n");
    }
    END_SEND(skb);
}


/*	Set MAC for inteface ifn*/
int  nsa_dev_set_mac(struct nsa_dev *nsa, int ifn, char * mac)
{
    u_int32_t macl32;
    u_int32_t mach16;
    int ret;
    if(ifn>7||ifn<0) {
        printk(KERN_ERR "invalid ifn parameter:%d\n", ifn);
        return -EINVAL;
    }
    memcpy(&mach16,mac,2); mach16 &= 0xffff;
	mach16 = htons(mach16);
    memcpy(&macl32,mac+2,4);
	macl32 = htonl(macl32);

    /* write low 32 bit*/
    ret = nsa_hw_spm(nsa, NSA_HW_ACTION_WRITE, NSA_SPM_ID_PIF_MAC_H16, ifn, &mach16);
    if(ret!=0) {
        printk(KERN_ERR "error to write spm: NSA_SPM_ID_PIF_MAC_H16, ifn=%d\n", ifn);
        return ret;
    }
    ret = nsa_hw_spm(nsa, NSA_HW_ACTION_WRITE, NSA_SPM_ID_PIF_MAC_L32, ifn, &macl32);
    if(ret!=0) {
        printk(KERN_ERR "error to write spm: NSA_SPM_ID_PIF_MAC_H16, ifn=%d\n", ifn);
        return ret;
    }
    return 0;
}

    
/*	Get MAC of interface ifn */
int  nsa_dev_get_mac(struct nsa_dev *nsa, int ifn, char * mac)
{
    u_int32_t macl32;
    u_int32_t mach16;
    int ret;
    if(ifn>7||ifn<0) {
        printk(KERN_ERR "invalid ifn parameter:%d\n", ifn);
        return -EINVAL;
    }
    
    /* write low 32 bit*/
    ret = nsa_hw_spm(nsa, NSA_HW_ACTION_READ, NSA_SPM_ID_PIF_MAC_H16, ifn, &mach16);
    if(ret!=0) {
        printk(KERN_ERR "error to write spm: NSA_SPM_ID_PIF_MAC_H16, ifn=%d\n", ifn);
        return ret;
    }
    ret = nsa_hw_spm(nsa, NSA_HW_ACTION_READ, NSA_SPM_ID_PIF_MAC_L32, ifn, &macl32);
    if(ret!=0) {
        printk(KERN_ERR "error to write spm: NSA_SPM_ID_PIF_MAC_H16, ifn=%d\n", ifn);
        return ret;
    }
	mach16 = ntohs(mach16);
	macl32 = ntohl(macl32);
    memcpy(mac,&mach16,2); mach16 &= 0xffff;
    memcpy(mac+2,&macl32,4);

    return 0;
}

static int  nsa_spi_set_mac(struct net_device *netdev, void *p)
{
    struct nsa_spi * spi= netdev_priv(netdev);
    nsa_dev_set_mac(spi->nsa, spi->ifn, p); 
    return 0;
}


static int  nsa_spi_get_mac(struct net_device *netdev, void *p)
{
    struct nsa_spi * spi= netdev_priv(netdev);
    nsa_dev_get_mac(spi->nsa, spi->ifn, p); 
    return 0;
}


void nsa_phy_power_up(struct nsa_spi *spi)
{
    struct nsa_dev *nsa=spi->nsa;
    struct nsa_spi_hw *hw=&spi->hw;
    int ifn=spi->ifn;
    uint16_t mii_reg = 0;
	/* Just clear the power down bit to wake the phy back up */
	if (hw->media_type == nsa_media_type_copper) {
		/* according to the manual, the phy will retain its
		 * settings across a power-down/up cycle */
		nsa_hw_read_mdio(nsa, ifn, PHY_CTRL, &mii_reg);
		mii_reg &= ~MII_CR_POWER_DOWN;
		nsa_hw_write_mdio(nsa, ifn, PHY_CTRL, mii_reg);
	}
}

/**
 * nsa_spi_open - Called when a network interface is made active
 * @netdev: network interface device structure
 *
 * Returns 0 on success, negative value on failure
 *
 * The open entry point is called when a network interface is made
 * active by the system (IFF_UP).  At this point all resources needed
 * for transmit and receive operations are allocated, the interrupt
 * handler is registered with the OS, the watchdog timer is started,
 * and the stack is notified that the interface is ready.
 **/
static int
nsa_spi_open(struct net_device *netdev)
{
	struct nsa_spi *spi = netdev_priv(netdev);
    /* disallow open during test */
	//if (test_bit(__NSA_DRIVER_TESTING, &nsa_adapter->flags))
	//	return -EBUSY;
    nsa_phy_power_up(spi);
    //if need!
	nsa_phy_power_up(spi);
//    nsa_spi_up(spi);
	return 0;
}


int nsa_dev_disable_phy(struct nsa_dev *nsa, int ifn)
{
    struct nsa_phy ifcfg;
    int ifreg = SSR_PHY_IF0 + (ifn<<2); /* i.e.: ifn*4 */
    if(ifn>7||ifn<0) {
        printk(KERN_ERR "invalid ifn parameter:%d\n", ifn);
        return -EINVAL;
    }
    ifcfg.data = 0;
    ifcfg.tx_enable = 0;
	ifcfg.rx_enable = 0;
    return nsa_hw_write_reg(nsa, ifreg, ifcfg.data);
}


void nsa_phy_power_down(struct nsa_spi *spi)
{
    struct nsa_dev *nsa=spi->nsa;
    struct nsa_spi_hw *hw=&spi->hw;
    int ifn=spi->ifn;
    uint16_t mii_reg = 0;
	/* Just clear the power down bit to wake the phy back up */
	if (hw->media_type == nsa_media_type_copper) {
		/* according to the manual, the phy will retain its
		 * settings across a power-down/up cycle */
		nsa_hw_read_mdio(nsa, ifn, PHY_CTRL, &mii_reg);
		mii_reg |= MII_CR_POWER_DOWN;
		nsa_hw_write_mdio(nsa, ifn, PHY_CTRL, mii_reg);
	}
}

static void nsa_spi_down(struct nsa_spi *spi)
{
	int ifn=spi->ifn;
	struct net_device *netdev=spi->netdev;
	struct nsa_dev *nsa=spi->nsa;

	nsa_dev_disable_phy(nsa, ifn);
	//netif_poll_disable(nsa_adapter->netdev);
	netif_carrier_off(netdev);
//	netif_stop_queue(netdev);
	//nsa_phy_reset(spi);
	nsa_phy_power_down(spi);
}

/**
 * nsa_spi_close - Disables a network interface
 * @netdev: network interface device structure
 *
 * Returns 0, this is not allowed to fail
 *
 * The close entry point is called when an interface is de-activated
 * by the OS.  The hardware is still under the drivers control, but
 * needs to be disabled.  A global MAC reset is issued to stop the
 * hardware, and all transmit and receive resources are freed.
 **/

static int
nsa_spi_close(struct net_device *netdev)
{
	struct nsa_spi *spi = netdev_priv(netdev);

    //WARN_ON(test_bit(__NSA_RESETTING, &nsa_adapter->flags));
//    del_timer_sync(&spi->link_timer);
    nsa_spi_down(spi);
	return 0;
}


int nsa_dev_xmit_packet(struct nsa_dev *nsa, struct sk_buff *skb, int lif, int pif)
{
	int hold;
	struct nsa_enabler *enabler = nsa->nsa_handler;
	struct nsa_header *head;
    struct nsa_tx_info *txinfo = skb_nsa_txinfo(skb);
    int mark=-1, prio;

    int ret=0;
    if(txinfo){
		enabler->nsa_enabler_counter[10]++;
		//if((nsa->status & 0x1) == NSA_STATUS_TEST)
		//   nsa_add_flow(skb, 0, NULL, NULL, NULL, NULL);
#if 0
		if(nsa_enabler_egress(enabler, skb) == NSA_SSN_PKT_RELEASED)
            return NETDEV_TX_OK;
#endif
	}
    nsa->drv_stats.tx_pkts_packet++;
    skb = nsa_dev_make_header(nsa, skb);
    if(!skb) {
        printk(KERN_ERR "error to make header...\n");
        return NETDEV_TX_OK;
    }
    head = (struct nsa_header*)skb_push(skb, NSA_HEADER_LEN);
	memset(head,0,NSA_HEADER_LEN);
    nsa_dev_e_lif(head) = lif;
    nsa_dev_e_pif(head) = pif;

#if 0
    if(mark>=0 && mark <NSA_MAX_QOS_ID) {
        if((prio = nsa_qosid_mark_map[enabler->nsa_id][mark])!= NSA_QOS_ID_INVALID) {
            nsa_dev_qos_prio(head) = prio;
            nsa_dev_qos_id(head) = mark;
        }
    }
#endif
    nsa_dev_pkttype(head) = NSA_PACKET_T;
	skb_pull(skb, NSA_HEADER_LEN);

	if(nsa->hold) 
		hold = 0;
	else 
		hold = 1;
	ret = nsa_skb_xmit(nsa,&(nsa->queue[QUEUE_TX_T].queue),skb,hold);
	if(ret==NETDEV_TX_OK) 
		return 0;
#if 0
	if(nsa->status & NSA_SATUS_TX_KTHREAD) {
		wake_up(&nsa->xmit_wait_queue);
	}
#endif
    return ret;

}

static int  nsa_spi_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
{
    struct nsa_spi *spi=netdev_priv(netdev);
    struct nsa_dev *nsa=spi->nsa;

	nsa->nsa_handler->nsa_enabler_counter[24]++;
    if (unlikely(skb->len <= 0)) {
        dev_kfree_skb_any(skb);
        spi->net_stats.tx_dropped++;
        return NETDEV_TX_OK;
    }
	nsa->nsa_handler->nsa_enabler_counter[22]++;
//   netdev->trans_start = jiffies;

    if(nsa_dev_xmit_packet(nsa,skb,netdev->sempsec_lif, spi->ifn)==NETDEV_TX_BUSY){
        //netif_stop_queue(netdev);
        dev_kfree_skb(skb);
        spi->net_stats.tx_dropped++;
    }else{
        spi->net_stats.tx_packets++;
        spi->net_stats.tx_bytes += skb->len ;
    }
	return NETDEV_TX_OK;
}

static int  nsa_spi_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
{
    return 0;
}


struct nsa_enabler* get_nsa_by_index(unsigned int index)
{
    if((index<MAX_SCI_NUMS)&&nsa_scis[index])
        return nsa_scis[index];
    else
        return NULL;
}


int nsa_dev_add_lif(struct nsa_dev *nsa, int lif, struct net_device *pdev, int vid)
{
    struct nsa_spi *spi=netdev_priv(pdev);
    int ifn, key, data;
    ifn = spi->ifn;
    key=(ifn<<12)|vid;
    /* ingress lif table */
    data = (1<<12)|lif;
    nsa_hw_spm(nsa, NSA_HW_ACTION_WRITE, NSA_SPM_ID_INGRESS_LIF, 
               key, &data);
    /* ingress lif property table, set la and zone */
    data=0;
    nsa_hw_spm(nsa, NSA_HW_ACTION_WRITE, NSA_SPM_ID_INGRESS_LIFP, 
               lif, &data);

    /* egress lif property table */
    data=(1<<3)|ifn;
    nsa_hw_spm(nsa, NSA_HW_ACTION_WRITE, NSA_SPM_ID_EGRESS_LIFP,
               lif, &data);
    
    return 0;
}

int nsa_add_lif(struct net_device *pdev, struct net_device *ldev, int vid)
{
    static int pif=0;
    int lif;
    int i;
    struct nsa_enabler *enabler=get_nsa_by_index(pdev->nsa_id);

    /* first process LA interfaces */
    if(vid == NSA_LIF_LA){
        for(i=NSA_MAX_PIF_NUM+1; i<=NSA_MAX_LA_NUM; i++) {
            if(!enabler->nsa_lif_pool[i]) {
                enabler->nsa_lif_pool[i] = ldev;
                lif = i;
		return lif;
            }
        }
        if(i == NSA_MAX_LA_NUM+1) {
            return -EBUSY;
        }
    }
    /* then physical interfaces, we always assume the sequences by default */
    if(vid == NSA_LIF_PHY){
	enabler->nsa_lif_pool[pif+1] = ldev;
	lif = ++pif;
    }else
	lif = nsa_lif_alloc(enabler, ldev);

    if((vid<0)||(vid>4095)) {
        printk(KERN_ERR "invalid vid parameter:%d\n", vid);
        return -EINVAL;
    }

    if(!lif){
	printk("Unable to allocate logical interface id for SempSec\n");
	return -EBUSY;
    }
    ldev->sempsec_lif = lif;
    ldev->nsa_id = pdev->nsa_id;
    enabler->vlan_map[pdev->sempsec_lif-1][vid]=lif;
    nsa_dev_add_lif(enabler->nsa_dev, lif, pdev, vid);
    return lif;
}

/**
 * nsa_spi_setup - NSA spi Initialization Routine
 * @nsa
 * Returns 0 on success, negative on failure
 *
 * nsa_spi_setup initializes NSA spis from nsa, register spi interfaces to OS
 * 
 **/

int nsa_spi_setup(struct nsa_dev *nsa)
{
	struct net_device *netdev;
	struct nsa_spi *spi;
    int ifn;
    int i, err;
    u_int16_t ports = nsa->ports;
    nsa->spis = (struct net_device**)kmalloc(sizeof(struct net_device*)*(ports),GFP_KERNEL);
	if(!nsa->spis) {
		NPRINTK(nsa, SPI, ERR, "Malloc nsa->netdev error\n");
		err = -ENOMEM;
		goto error;
	}

    for(ifn=0; ifn<ports; ifn++){
		char ifname[10];
		
		//need to change this!
		netdev = kmalloc(sizeof(struct nsa_spi) + sizeof(struct net_device));
		netdev->priv = (unsigned char *)netdev + sizeof(net_device);
		//netdev = alloc_etherdev(sizeof(struct nsa_spi));
		if(!netdev){
			NPRINTK(nsa, SPI, ERR, "Malloc netdev %d error\n", ifn);
			err = -ENOMEM;
			goto error;
		}
        netdev->open = &nsa_spi_open;
		netdev->stop = &nsa_spi_close;
		netdev->hard_start_xmit = &nsa_spi_xmit_frame;
//		netdev->get_stats = &nsa_spi_get_stats;
		netdev->set_mac_address = &nsa_spi_set_mac;
//		netdev->change_mtu = &nsa_spi_change_mtu;
		netdev->do_ioctl = &nsa_spi_ioctl;
//		nsa_spi_set_ethtool_ops(netdev);
//		netdev->tx_timeout = &nsa_spi_tx_timeout;
		netdev->watchdog_timeo = 5 * HZ;
		netdev->features |= NETIF_F_LLTX;
        netdev->nsa_id = nsa->dev_id;
        nsa->spis[ifn]=netdev;

		spi=netdev_priv(netdev);
		spi->netdev = netdev;
		spi->nsa = nsa;
		spi->ifn = ifn;

        if(ifn<4){
            //printk("base_board_map = 0x%x\n", nsa->dna.base_board_map);
            if(!(nsa->dna.base_board_map & (1<<ifn))) {
                spi->hw.media_type = nsa_media_type_copper;
            }else
                spi->hw.media_type = nsa_media_type_fiber;
        } else{
            //printk("addon_board_map = 0x%x\n", nsa->dna.addon_board_map);
            if(!(nsa->dna.addon_board_map & (1<<(ifn-4)))) {
                spi->hw.media_type = nsa_media_type_copper;
            }else
                spi->hw.media_type = nsa_media_type_fiber;
        }
        printk(KERN_INFO "spi%d.%d port %s\n",  nsa->dev_id, ifn, spi->hw.media_type? "fiber":"copper");
		spi->hw.wait_autoneg_complete = 1;
		spi->hw.adaptive_ifs = 1;
		spi->hw.autoneg = 1;

        /* Copper options */
		if (spi->hw.media_type == nsa_media_type_copper) {
			spi->hw.mdix = AUTO_ALL_MODES;
			spi->hw.disable_polarity_correction = 0;
			spi->hw.master_slave = nsa_ms_auto;
		}
#if 0
#ifdef LINUX2620
        INIT_WORK(&spi->watchdog_task, nsa_spi_watchdog_task);
#else
        INIT_WORK(&spi->watchdog_task, nsa_spi_watchdog_task,&spi->watchdog_task);
#endif
#endif
#if 0
        init_timer(&spi->link_timer);
        spi->link_timer.function = &nsa_spi_watchdog;
        spi->link_timer.data = (unsigned long) spi;
#endif
		nsa_spi_reset(netdev);

        /* mac address is: 00:3a:dna:dna:dna:dna+ifn */
        //memcpy(&base_mac[2], &nsa->dna, 4);
        //base_mac[5]+=ifn;
		memcpy(&base_mac[0],&nsa->dna.mac[0],6);
		base_mac[5]+=ifn;
        nsa_spi_set_mac(netdev, base_mac);
		nsa_spi_get_mac(netdev, spi->hw.mac_addr);
        //memcpy(spi->hw.mac_addr, base_mac, netdev->addr_len);
        memcpy(netdev->dev_addr, spi->hw.mac_addr, netdev->addr_len);
		memcpy(netdev->perm_addr, spi->hw.mac_addr, netdev->addr_len);
	
		for (i = 0; i < 6; i++)
			printk("%2.2x%c", netdev->dev_addr[i], i == 5 ? '\n' : ':');

		sprintf(ifname, "spi%d.%d", nsa->dev_id, ifn);
		strcpy(netdev->name, ifname);

		netif_carrier_off(netdev);
//		netif_stop_queue(netdev);

		//need to change! add to the chain, a new chain?
		TAILQ_INSERT_TAIL(&alldevs, netdev, dv_list);
//		if ((err = register_netdev(netdev)))
//			goto error;
        //need to change! 
		nsa_add_lif(netdev, netdev, NSA_LIF_PHY);
	}
	return 0;

error:
    if(nsa->spis) {
        for(i=0; i<nsa->ports; i++) {
            if(nsa->spis[i]) {
				printk("init spi failed!\n");
//                unregister_netdev(nsa->spis[i]);
//              free_netdev(nsa->spis[i]);
            }
        }
        kfree(nsa->spis);
    }
    return err;
}


void nsa_probe(struct npcp_controller *sci)
{
    //INIT_WORK(&nsa->reset_task, nsa_dev_discover_task);
    //schedule_work(&nsa->reset_task); 
    struct nsa_dev *nsa = sci_nsa(sci);
    struct nsa_version sver;
    struct nsa_enabler *enabler;
	printk("<0>nsa=%x\n",nsa);

    printk(KERN_INFO "nsa device %d discovering...\n", nsa->dev_id); 
    if(nsa_dev_probe(nsa) <0){
        printk(KERN_ERR "nsa device %d discovering error\n", nsa->dev_id);
    }else
        printk(KERN_INFO "nsa device %d discovered\n", nsa->dev_id);
    
    printk("\tnsa h/w dna: 0x%08x\n", nsa->hw_dna);
    printk("\tnsa base board s/n %s, property 0x%x\n", 
           nsa->dna.base_board_sn, nsa->dna.base_board_map);
    if(nsa->dna.addon_board_map & 0x80){ 
        nsa->ports = 8;
        printk("\tnsa add-on board s/n %s, property 0x%x\n", 
               nsa->dna.addon_board_sn, nsa->dna.addon_board_map);
    }else
        nsa->ports = 4;
    if(nsa->dna.tcam) 
        printk("\tnsa tcam is valid\n");

    /* reset nsa */
    if(nsa_dev_reset(nsa) <0){
        printk(KERN_ERR "nsa device %d resetting error\n", nsa->dev_id);
        return;
    }

    /* now nsa_spi should be created */
#if 0
#ifdef LINUX2620
    INIT_WORK(&nsa->aging_task, nsa_aging_task);
#else
    INIT_WORK(&nsa->aging_task, nsa_aging_task,&nsa->aging_task);
#endif
    nsa->xmit_task = kthread_create(nsa_xmit_task, nsa, "knsaxmit_%dd", nsa->dev_id);
	if (IS_ERR(nsa->xmit_task)) {
		panic("kernel_thread() failed to create\n");
	}
#endif
    //INIT_WORK(&nsa->stating_task, nsa_dev_stating_task);
    
    /* fixmed... */
    enabler = nsa_enabler_allocate(nsa->dev_id);
    if(!enabler) {
		nsa->ports = 0;
        printk(KERN_ERR "error to allocate nsa handler...\n");
        return;
    }
    enabler->nsa_dev = nsa;
//    nsa_dev_hook_register(enabler);				//need to open this function if necessary!
    nsa->nsa_handler = enabler;

    nsa_dev_init(nsa);

    printk("Semptian SempSec network security accelarator card initialized\n");
#if 0
    wake_up_process(nsa->xmit_task);
#endif
    nsa_spi_setup(nsa);		//need change! set net_device struct!
}

#ifdef NSA
/*
 * user s(g)etsockopt access
 */

//NSACTL_SO_SET_ACL
//fixme
// 
static struct nsa_acl _nsa_acl;
static int
nsactl_do_acl(void __user *user,int *len)
{
	int ret;
	struct nsa_acl *tmp=&_nsa_acl;
	struct nsa_enabler *enabler;

	ret=-ENOENT;
	if (copy_from_user(tmp, user, sizeof(*tmp)) != 0)
		return -EFAULT;

	enabler = get_nsa_by_index(tmp->nsa_id);
	if(!enabler)
        return -ENODEV;
	printk(KERN_DEBUG"acl: nsa_id=%d len=%d action=%d acl_id=%d\n",
		   tmp->nsa_id,sizeof(*tmp),tmp->action,tmp->acl_id);
	switch(tmp->action){
	case ACL_ACTION_READ:
		if(enabler->nsa_get_acl)
			ret= (*enabler->nsa_get_acl)(tmp->nsa_id,tmp->acl_id,tmp);
		break;
	case ACL_ACTION_WRTIE:
		if(enabler->nsa_set_acl)
			ret= (*enabler->nsa_set_acl)(tmp->nsa_id,tmp->acl_id,tmp);
		break;
	case ACL_ACTION_FLUSH:
		if(enabler->nsa_dev_clear_acl)
			ret=(*enabler->nsa_dev_clear_acl)(tmp->nsa_id);
	case ACL_ACTION_DOWNLOAD:
		break;
	case ACL_ACTION_SETLOG:
		if(enabler->nsa_set_acl_property)
		    ret= (*enabler->nsa_set_acl_property)(tmp->nsa_id,tmp->acl_id,
												  tmp->mirror,ACL_ACTION_SETLOG);
		break;
	default:
		printk("nsactl_do_acl: unknow acl action\n");
		ret = -EINVAL;
	}
	return ret;
}

//NSACTL_SO_SET_QOS
static struct nsa_qos _nsa_qos;
static int 
nsactl_do_qos(void __user *user, unsigned int len)
{
	int ret;
	struct nsa_qos *tmp = &_nsa_qos;
	struct nsa_enabler *enabler;

	ret=-ENOENT;
	if(len!=sizeof(struct nsa_qos)){
		ndebug("length %u != %u\n",len,
			   (unsigned int)sizeof(struct nsa_qos));
		return -EINVAL;
		}
	if (copy_from_user(tmp, user, sizeof(*tmp)) != 0)
		return -EFAULT;

	printk(KERN_DEBUG"qos: nsa_id=%d len=%u qos=%u min=%d max=%d action=%d\n",
	        tmp->nsa_id, sizeof(*tmp),tmp->qos_id,tmp->min,tmp->max,tmp->action);
	enabler = get_nsa_by_index(tmp->nsa_id);
	if(!enabler)
        return -ENODEV;
	switch(tmp->action){
	case QOS_ACTION_WRTIE:
		if(enabler->nsa_set_qos)
			ret=(*enabler->nsa_set_qos)(tmp->nsa_id,tmp->qos_id,tmp->max,tmp->min, tmp->prio);
		break;
	case QOS_ACTION_READ:
		if(enabler->nsa_get_qos)
			ret=(*enabler->nsa_get_qos)(tmp->nsa_id,tmp->qos_id,&tmp->max, &tmp->min, &tmp->prio);
		break;
	default:
		printk("nsactl_do_qos: unknow qos action\n");
		ret = -EINVAL;
	}
	return ret;
}

//NSACTL_SO_SET_HW
static struct nsa_hw_op _nsa_hw;
static int 
nsactl_do_hw(void __user *user, struct nsa_hw_op **hw,unsigned int len)
{
	int ret,l;
	struct nsa_hw_op *tmp=&_nsa_hw,*hw_op;
	struct nsa_reg *reg;
	struct nsa_dimm *dimm;
	struct nsa_spm *spm;
	struct nsa_tcam *tcam;
	struct nsa_enabler *enabler;
	struct nsa_rldram *rldram;
	struct nsa_mdio *mdio;
	struct nsa_counter *counter;
	struct nsa_status *status;

    if(hw) {
        hw_op = *hw;
    }else
        hw_op = NULL;
	ret=-ENOENT;
	l=sizeof(struct nsa_hw_op);
	if(len < l)
		return -EINVAL;
	
	if (copy_from_user(tmp, user, sizeof(*tmp)) != 0)
		return -EFAULT;

	enabler = get_nsa_by_index(tmp->nsa_id);
	if(!enabler)
			return -ENODEV;
	if(tmp->action != NSA_HW_ACTION_READ &&
	   tmp->action != NSA_HW_ACTION_WRITE &&
	   tmp->action != NSA_HW_ACTION_WRITE_WAIT &&
	   tmp->action != NSA_HW_ACTION_FLUSH &&
       tmp->action != NSA_HW_ACTION_SEARCH){
		printk(KERN_ERR "nsactl_do_hw: unknow hardware action\n");
		return -EINVAL;
	}

	//printk(KERN_DEBUG"hw: nsa_id=%d len=%d type=%d action=%d\n",
	//	   tmp->nsa_id,sizeof(*tmp),tmp->hw_type,tmp->action);
	switch(tmp->hw_type){
	case NSA_HW_TYPE_REG:
		l+=sizeof(struct nsa_reg);
		if(len != l){
			return -EINVAL;
		}
		hw_op=(void *)kmalloc(l,GFP_KERNEL);
		if(!hw_op){
			return -ENOMEM;
		}
		reg=(struct nsa_reg *)(&hw_op->data);
		if (copy_from_user(hw_op, user, l) != 0){
			kfree(hw_op);
			return -EFAULT;
		}

		if(enabler->nsa_access_register)
			ret=(*enabler->nsa_access_register)(enabler->nsa_dev, tmp->action,reg->reg, &reg->data);
		printk(KERN_DEBUG"nsa %d reg 0x%05x = 0x%x\n",tmp->nsa_id, reg->reg, reg->data);

        break;
	case NSA_HW_TYPE_DIMM:
		l+=sizeof(struct nsa_dimm);
		if(len != l){
			ndebug("length %u != %u\n",len,
				   (unsigned int)sizeof(struct nsa_dimm));
			return -EINVAL;
		}
		hw_op=(void *)kmalloc(l,GFP_KERNEL);
		if(!hw_op){
			NPRINTK(enabler->nsa_dev, SPI, ERR, "Malloc hw(dimm) error\n");
			return -ENOMEM;
		}

		dimm=(struct nsa_dimm*)&(hw_op->data);
		if (copy_from_user(hw_op, user, l) != 0){
            kfree(hw_op);
            return -EFAULT;
		}
        if(enabler->nsa_access_dimm)
			ret=(*enabler->nsa_access_dimm) \
			(enabler->nsa_dev,tmp->action,dimm->type,dimm->mask,dimm->addr,dimm->data);

		/*
		{
			int i;

			printk(KERN_DEBUG"dimm: nsa %d address 0x%x\ndata",tmp->nsa_id,dimm->addr);
			for(i=0;i<8;i++){
				printk(KERN_DEBUG"\t 0x%016llx",*(dimm->data+i));
				if(i%2==1)
			printk(KERN_DEBUG"\n");
			}
		}
		*/
		break;
	case NSA_HW_TYPE_SPM:
		l+=sizeof(struct nsa_spm);
		if(len != l){
			ndebug("length %u != %u\n",len,
				   (unsigned int)sizeof(struct nsa_spm));
			return -EINVAL;
		}
		hw_op=(void *)kmalloc(l,GFP_KERNEL);
		if(!hw_op){
			NPRINTK(enabler->nsa_dev, SPI, ERR, "Malloc hw(spm) error\n");
			return -ENOMEM;
		}

		spm = (struct nsa_spm *)(&hw_op->data);
		if (copy_from_user(hw_op, user, l) != 0){
			kfree(hw_op);
			return -EFAULT;
		}
		if(enabler->nsa_access_spm)
			ret=(*enabler->nsa_access_spm)
			(enabler->nsa_dev,tmp->action,spm->spm_id,spm->addr,&spm->data);
		printk(KERN_DEBUG"spm: len %u nsa %d spm_id %d address 0x%x data 0x%x\n",
			   len,tmp->nsa_id,spm->spm_id,spm->addr,spm->data);
		break;
	case NSA_HW_TYPE_TCAM:
		l+=sizeof(struct nsa_tcam);
		if(len != l){
			ndebug("length %u != %u\n",len,
				   (unsigned int)sizeof(struct nsa_tcam));
			return -EINVAL;
		}
		hw_op=(void *)kmalloc(l,GFP_KERNEL);
		if(!hw_op){
			printk(KERN_ERR "Malloc %d hw(tcam) error\n", l);
			return -ENOMEM;
		}

		tcam=(struct nsa_tcam *)(&hw_op->data);
		if (copy_from_user(hw_op, user, l) != 0){
			kfree(hw_op);
			return -EFAULT;
		}
		if(enabler->nsa_access_tcam)
			ret=(*enabler->nsa_access_tcam)
			(enabler->nsa_dev,tmp->action,tcam->seg,
			 &tcam->addr,tcam->tcam_act,&tcam->hit,tcam->data);
		break;
	case NSA_HW_TYPE_RLDRAM:
		l+=sizeof(struct nsa_rldram);
		if(len != l){
			ndebug("length %u != %u\n",len,
				   (unsigned int)sizeof(struct nsa_rldram));
			return -EINVAL;
		}
		hw_op=(void *)kmalloc(l,GFP_KERNEL);
		if(!hw_op){
			NPRINTK(enabler->nsa_dev, SPI, ERR, "Malloc hw(dimm) error\n");
			return -ENOMEM;
		}

		rldram=(struct nsa_rldram*)(&hw_op->data);
		if (copy_from_user(hw_op, user, l) != 0){
			kfree(hw_op);
			return -EFAULT;
		}
		if(enabler->nsa_access_rldram)
			ret=(*enabler->nsa_access_rldram)
			(enabler->nsa_dev,tmp->action,tmp->hw_type,rldram->mask,rldram->addr, rldram->data);
		break;
	case NSA_HW_TYPE_MDIO:
		l+=sizeof(struct nsa_mdio);
		if(len != l){
			ndebug("length %u != %u\n",len,
				   (unsigned int)sizeof(struct nsa_mdio));
			return -EINVAL;
		}
		hw_op=(void *)kmalloc(l,GFP_KERNEL);
		if(!hw_op){
			NPRINTK(enabler->nsa_dev, SPI, ERR, "Malloc hw(dimm) error\n");
			return -ENOMEM;
		}

		mdio=(struct nsa_mdio*)(&hw_op->data);
		if (copy_from_user(hw_op, user, l) != 0){
			kfree(hw_op);
			return -EFAULT;
		}
		if(enabler->nsa_access_mdio)
			ret=(*enabler->nsa_access_mdio)
			(enabler->nsa_dev,tmp->action,mdio->ifn,mdio->reg_mdio,&mdio->data);
		printk(KERN_DEBUG"mdio: nsa %d ifn spi%d reg 0x%x data 0x%x\n",
			   tmp->nsa_id,mdio->ifn,mdio->reg_mdio,mdio->data);

		break;
    case NSA_HW_TYPE_COUNTER:
		l+=sizeof(struct nsa_counter);
		if(len != l){
			ndebug("length %u != %u\n",len, l);
			return -EINVAL;
		}
		hw_op=(void *)kmalloc(l,GFP_KERNEL);
		if(!hw_op){
			NPRINTK(enabler->nsa_dev, SPI, ERR, "Malloc hw(dimm) error\n");
			return -ENOMEM;
		}

		counter=(struct nsa_counter*)(&hw_op->data);
		if (copy_from_user(hw_op, user, l) != 0){
			kfree(hw_op);
			return -EFAULT;
		}
		if(enabler->nsa_access_counter)
			ret=(*enabler->nsa_access_counter)
			(enabler->nsa_dev,tmp->action, 1, &counter->counter_address,&counter->data);
		break;
	case NSA_HW_TYPE_STATUS:
		l += sizeof(struct nsa_status);
		if(len != l){
			ndebug("length %u != %u\n",len,l);
			return -EINVAL;
		}
		hw_op = (void *)kmalloc(l,GFP_KERNEL);
		if(!hw_op){
			NPRINTK(enabler->nsa_dev, SPI, ERR, "Malloc hw(dimm) error\n");
			return -ENOMEM;
		}
		status = (struct nsa_status *)(&hw_op->data);
		if (copy_from_user(hw_op, user, l) != 0){
			kfree(hw_op);
			return -EFAULT;
		}
		if(enabler->nsa_access_status_bit)
			(*enabler->nsa_access_status_bit)
			(enabler->nsa_dev,&(status->status),tmp->action);
		ret = 0;
		printk(KERN_DEBUG"status: nsa %u status %x\n",hw_op->nsa_id,status->status);
		break;
	default:
		printk("nsactl_do_hw: unknow hw type\n");
		ret = -EINVAL;
	}
    if(hw) (*hw) = hw_op;
    else kfree(hw_op);
	return ret;
}

static struct nsa_flash _nsa_flash;
static int nsactl_do_flash(void __user *user, unsigned int len)
{
	int ret;
	struct nsa_flash *tmp=&_nsa_flash;
    struct nsa_enabler *enabler = get_nsa_by_index(tmp->nsa_id);

	ret=-ENOENT;
	if(len != sizeof(struct nsa_flash)){
		ndebug("length %u != %u\n",len,
			   (unsigned int)sizeof(struct nsa_flash));
		return -EINVAL;
	}
	//ndebug("flash: nsa %u action %d\n",_nsa_flash.nsa_id,_nsa_flash.action);
    if (copy_from_user(tmp, user, sizeof(*tmp)) != 0)
		return -EFAULT;

	if(enabler->nsa_access_flash)
		ret = (*enabler->nsa_access_flash)(enabler->nsa_dev,
						tmp->action, tmp->address, tmp->length, tmp->data);
    return ret<0? -EFAULT: 0;
}

static u_int8_t _ssn_data[sizeof(struct nsa_ssn)];
static int nsactl_do_ssn(void __user *user, unsigned int len)
{
	int ret=0;
	struct nsa_ssn *tmp=(struct nsa_ssn *)_ssn_data;
    struct nsa_enabler *enabler;

	ret=-ENOENT;
	if (len != sizeof(struct nsa_ssn)){
		ndebug("length %u != %u\n",len,
			   (unsigned int)sizeof(struct nsa_ssn));
		return -EINVAL;
		}
	if (copy_from_user(tmp, user, sizeof(*tmp)) != 0)
		return -EFAULT;

    enabler = get_nsa_by_index(tmp->nsa_id);
    if(!enabler) {
        return -ENODEV;
    }

    switch (tmp->action) {
	case SSN_ACTION_SET:
		if(enabler->nsa_set_ssn)
			ret = (*enabler->nsa_set_ssn)(tmp->nsa_id, tmp->pos, tmp->start, tmp->len, tmp->data);
		break;
	case SSN_ACTION_GET:
		if(enabler->nsa_get_ssn)
			ret = (*enabler->nsa_get_ssn)(tmp->nsa_id, tmp->pos,
										  tmp->start, tmp->len, tmp->data);
        break;
	case SSN_ACTION_DEBUG:
		(*enabler->msg_enable) = (int)(tmp->data[0]&0xffff);
		ret = 0;
		break;
	case SSN_ACTION_ADD:
	case SSN_ACTION_DEL:
	case SSN_ACTION_MOD:
	case SSN_ACTION_TEST:
	case SSN_ACTION_CLEAR:
		if(enabler->nsa_input_ssn)
			ret = (*enabler->nsa_input_ssn)(tmp->nsa_id,tmp->action,tmp->data,&tmp->pos);
		break;
    default:
		printk("nsactl_do_ssn: unknow ssn action\n");
		ret = -EINVAL;
	}
	printk(KERN_DEBUG"ssn: nsa_id=%d len=%d pos=0x%x start=%d end=%d action=%d\n",
		   tmp->nsa_id,len,tmp->pos,tmp->start,tmp->len+tmp->start,tmp->action);
	printk("ssn data: data[0, 7] 0x%llx 0x%llx "
		   "0x%llx 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx \n",
		   tmp->data[0],tmp->data[1],tmp->data[2],
		   tmp->data[3],tmp->data[4],tmp->data[5],tmp->data[6],tmp->data[7]);
	return ret;
}

int nsactl_do_version(void __user *user, struct nsa_version *ver)
{
    int ret=0;
	//unsigned  int len;
    struct nsa_enabler *enabler;

	//if (len != sizeof(struct nsa_version)){
	//	ndebug("length %u != %u\n",len,
	//		   (unsigned int)sizeof(struct nsa_version));
	//	return -EINVAL;
	//}
	if (copy_from_user(ver, user, sizeof(struct nsa_version)) != 0)
		return -EFAULT;

    enabler = get_nsa_by_index(ver->nsa_id);
    if(!enabler) {
        return -ENODEV;
    }

	ret = nsa_dev_get_version(ver->nsa_id, ver);
    return ret;
}

static int
do_nsactl_set_ctl(struct sock *sk,	int cmd, void __user *user, unsigned int len)
{
	int ret;

	if (!capable(CAP_NET_ADMIN))
		return -EPERM;

	switch (cmd) {
	case NSACTL_SO_SET_ACL:
		ret = nsactl_do_acl(user,&len);
		break;
	case NSACTL_SO_SET_FLASH:
		ret = nsactl_do_flash(user,len);
		break;
	case NSACTL_SO_SET_SSN:
		ret = nsactl_do_ssn(user, len);
		break;
	case NSACTL_SO_SET_QOS:
		ret = nsactl_do_qos(user , len);
		break;
	case NSACTL_SO_SET_HW:
		ret =nsactl_do_hw(user ,NULL, len);
		break;
	default:
		printk("do_nsactl_set_ctl:  unknown request %i\n", cmd);
		ret = -EINVAL;
	}

	return ret;
}


static int
do_nsactl_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
{
	int ret;
	struct nsa_hw_op *get_hw=NULL;
	if (!capable(CAP_NET_ADMIN))
		return -EPERM;

	switch (cmd) {
	case NSACTL_SO_GET_VERSION:
		{
			struct nsa_version sver;
			
			if(*len != sizeof(sver)){
				ndebug("length %u != %u\n",*len,
					   (unsigned int)sizeof(struct nsa_version));
				return -EINVAL;
			}
			memset(&sver, 0, sizeof(sver));

			/*for debug
			sver.build=i++;
			ret = copy_to_user(user, &sver, sizeof(sver));
			break;
			*/

			nsactl_do_version(user, &sver);
			ret = copy_to_user(user, &sver, sizeof(sver));
			break;
		}
    case NSACTL_SO_GET_FLASH:
        {
			memset(&_nsa_flash, 0, sizeof(_nsa_flash));
			nsactl_do_flash(user, sizeof(_nsa_flash));
			ret = copy_to_user(user, &_nsa_flash, sizeof(_nsa_flash));
			break;

        }
	case NSACTL_SO_GET_SSN:
		{   
			/*for debug
			static int j=1;
			struct nsa_ssn *ssn=&_ssn_data;

			ssn->pos=j++;
			memset(&(ssn->data),5,64);
			ret = copy_to_user(user, ssn, *len);
			break;
			*/
			ret = nsactl_do_ssn(user, *len);
			if(ret<0)
				return ret;
			ret = copy_to_user(user, &_ssn_data, *len);
			if(ret<0)
				return ret;
			break;
		}
	case NSACTL_SO_GET_ACL:
		{
			ret = nsactl_do_acl(user,len);
			if(ret<0)
				return ret;
			ret = copy_to_user(user, &_nsa_acl,*len);
			if(ret<0)
				return ret;
			break;
		}
	case NSACTL_SO_GET_QOS:
		{
		    /*for debug
			static int k=1;

			_nsa_qos.qos_id=k++;
			_nsa_qos.min= k%100;
			_nsa_qos.max= _nsa_qos.min+1;
			ret = copy_to_user(user, &_nsa_qos, *len);
			break;
			*/

			ret = nsactl_do_qos(user, *len);
			if(ret<0)
				return ret;
			ret = copy_to_user(user, &_nsa_qos, *len);
			break;
		}
	case NSACTL_SO_GET_HW:
		{
			ret = nsactl_do_hw(user, &get_hw,*len);
			if(ret<0)
				goto hw_free;
			ret = copy_to_user(user, get_hw, *len);
			break;
		}
	default:
		printk("do_nsactl_get_ctl: unknown request %i\n", cmd);
		ret = -EINVAL;
	}
hw_free:
	if(get_hw)
		kfree(get_hw);
    return ret;
}

static struct nf_sockopt_ops nsactl_sockopts = {
	.pf		= PF_INET,
	.set_optmin	= NSACTL_BASE_CTL,
	.set_optmax	= NSACTL_SO_SET_MAX+1,
	.set		= do_nsactl_set_ctl,
	.get_optmin	= NSACTL_BASE_CTL,
	.get_optmax	= NSACTL_SO_GET_MAX+1,
	.get		= do_nsactl_get_ctl,
};

static int nsa_ctrl_init()
{
	int ret;
	ret = nf_register_sockopt(&nsactl_sockopts);
	if(ret<0)
		printk("nsactl: register sockopt failed %d\n", ret);
	return ret;
}

static void nsa_ctrl_deinit()
{
	nf_unregister_sockopt(&nsactl_sockopts);
}

#endif
